//
// Created by 郝羽 on 2018/2/9.
//

#ifndef RTC_XSWITCH2_REPORT_HPP
#define RTC_XSWITCH2_REPORT_HPP

#include <chrono>
#include <ctime>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <list>
#include <sstream>
#include <string>

#include "log.hpp"
#include "metric.hpp"

class Point {
public:
    int x;
    int y;
};

class DataSet {
public:
    std::string label;
    std::string color;
    std::list<Point> points;
};

class Graph {
public:
    std::string type;
    std::string id;
    std::string title;
    int x_min;
    int x_max;
    std::string x_label;
    std::string y_label;
    std::list<DataSet> datasets;
};

class KVDesc {
public:
    std::string name;
    std::string value;
    std::string desc;
};


class Report {
public:
    std::list<KVDesc> configs;
    std::list<Graph> graphs;
};


class Printer {
public:
    void Print(const Report& r) {
        std::ostringstream fmt;
        fmt << printHeader();
        for (auto cfg = r.configs.begin(); cfg != r.configs.end(); cfg++) {
            fmt << printHtmlConfig(*cfg);
        }
        for (auto g = r.graphs.begin(); g != r.graphs.end(); g++) {
            fmt << printHtmlGraph(*g);
        }
        fmt << printChartJS();
        for (auto g = r.graphs.begin(); g != r.graphs.end(); g++) {
            fmt << printHtmlGraphScript(*g);
        }
        fmt << printTrailer();

        auto now = std::chrono::system_clock::now();
        auto in_time_t = std::chrono::system_clock::to_time_t(now);
        char fn[100];
        strftime(fn, sizeof(fn), "/tmp/report-%Y%m%d-%H%M%S.html", std::localtime(&in_time_t));
        std::ofstream of(fn, std::ofstream::out);
        of << fmt.str();
        of.close();
        Log::Info() << "report write to " << std::string(fn) << std::endl;
    }

private:
    std::string printHeader() {
        std::ostringstream fmt;
        fmt << "<!DOCTYPE html>\n";
        fmt << "<html lang=\"zh-cn\">\n";
        fmt << "<head>\n";
        fmt << "    <meta charset=\"UTF-8\">\n";
        fmt << "    <title>测试报告</title>\n";
        fmt << "    <style type=\"text/css\">\n";
        fmt << "        .graph-container {\n";
        fmt << "          position: relative;\n";
        fmt << "          height: 40vh;\n";
        fmt << "          width: 80vw;\n";
        fmt << "        }";
        fmt << "        .config {\n";
        fmt << "          height: 5vh;\n";
        fmt << "          width: 80vw;\n";
        fmt << "        }\n";
        fmt << "    </style>\n";
        fmt << "</head>\n";
        fmt << "<body>\n";
        fmt << "  <h1>测试报告</h1>\n";
        return fmt.str();
    }

    std::string printTrailer() {
        std::ostringstream fmt;
        fmt << "  </body>\n"
               "</html>\n";
        return fmt.str();
    }

    std::string printHtmlConfig(const KVDesc& kvd) {
        std::ostringstream fmt;
        fmt << "  <div class=\"config\">\n"
               "    " << kvd.name << "<br/>\n"
               "    " << kvd.value << "<br/>\n"
               "    " << kvd.desc << "<br/>\n"
               "  </div>\n";
        return fmt.str();
    }

    std::string printHtmlGraph(const Graph& g) {
        std::ostringstream fmt;
        fmt << "      <div class=\"graph-container\">\n"
               "          <canvas id=\"" << g.id << "\"/>\n"
               "      </div>\n";
        return fmt.str();
    }

    std::string printChartJS() {
        return "<script src=\"https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.1/Chart.bundle.min.js\"></script>\n";
    }

    std::string printHtmlGraphScript(const Graph& g) {
        std::list<int> labels;



        std::ostringstream fmt;
        fmt << "  <script>\n";
        fmt << "    var ctx = document.querySelector('#" << g.id << "').getContext('2d');\n";
        fmt << "    new Chart.Line(ctx, {\n";
//        fmt << "      type: '" << g.type << "',\n";
        fmt << "      options: {\n";
        fmt << "        responsive: true,\n";
        fmt << "        maintainAspectRatio: false,\n";
        fmt << "        title: {\n";
        fmt << "            display: true,\n";
        fmt << "            text: '" << g.title << "'\n";
        fmt << "        },\n";
        fmt << "        scales: {\n";
        fmt << "            yAxes: [{\n";
        fmt << "               scaleLabel: {\n";
        fmt << "                   display: true,\n";
        fmt << "                   labelString: '" << g.y_label << "'\n";
        fmt << "               },\n";
        fmt << "            }],\n";
        fmt << "            xAxes: [{\n";
        fmt << "               scaleLabel: {\n";
        fmt << "                   display: true,\n";
        fmt << "                   labelString: '" << g.x_label << "'\n";
        fmt << "               }\n";
        fmt << "            }]\n";
        fmt << "        }\n";
        fmt << "      },\n";
        fmt << "      data: {\n";
        fmt << "              labels: [\n";
        for (auto x = g.x_min; x <= g.x_max; ++x) {
            fmt << "                       " << x;
            if (x != g.x_max) {
                fmt << ",";
            }
            fmt << "\n";
        }
        fmt << "              ],\n";
        fmt << "          datasets: [\n";
        for (auto ds = g.datasets.begin(); ds != g.datasets.end(); ds++) {
            fmt << "              {\n";
            fmt << "                  label: '" << ds->label << "',\n";
            if (!ds->color.empty()) {
                fmt << "                  borderColor: '" << ds->color << "',\n";
                fmt << "                  fill: 'false',\n";
            }

            fmt << "                  data: [\n";
            for (auto d = ds->points.begin(); d != ds->points.end(); d++) {
                fmt << "                      {x:" << d->x << ", y:" << d->y << "}";
                auto d2 = d;
                d2++;
                if (d2 != ds->points.end()) {
                    fmt << ",";
                }
                fmt << "\n";
            }

            fmt << "                  ],\n";
            fmt << "              }";
            auto ds2 = ds;
            ds2++;
            if (ds2 != g.datasets.end()) {
                fmt << ",";
            }
            fmt << "\n";
        }
        fmt << "          ]\n"
               "      }\n"
               "    });\n"
               "  </script>\n";
        return fmt.str();
    }
};

void report(int argc, char* argv[], MetricsByTime& metrics);

#endif //RTC_XSWITCH2_REPORT_HPP
